﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.GamerServices;


#if WINDOWS
using System.Threading;
#endif

namespace OpiConsole
{
    /// <summary>
    /// The level of usage you must have in order to use commands/CVars
    /// </summary>
    public enum UsageLevel
    {
        NONE,               //These commands should be used by the engine only
        DEBUGDEV,
        DEV,
        NORMAL
    }

    /// <summary>
    /// Defines who or what the print to the console.
    /// </summary>
    public enum ConsoleLevel
    {
        TXTFILE,
        DEBUG,
        NORM,
        WARN,
        FAIL
    }

    public struct AllowedInput
    {
        public string input;
        public string output;
        public string shiftOutput;

        public AllowedInput(string inp, string outp, string shift)
        {
            input = inp;
            output = outp;
            shiftOutput = shift;
        }
    }

    /// <summary>
    /// Console class which allows you to enter commands to get results within the game
    /// </summary>
    public class Console : DrawableGameComponent
    {
        //Game Managers/Objects
        private Game m_pGame;
        private Object m_pGameManager; //Holds a reference to the game manager supplied
        private ContentManager m_pContent;
        private SpriteBatch m_pSpriteBatch;
        private StorageContainer m_pContainer;

        //Console Data
        private List<String> m_lConsoleText;
        private List<String> m_lHistoryText;
        private List<Color> m_lLineColour;
        Dictionary<String, Command> m_dCommands;
        Dictionary<String, CVar> m_dCVars;
        private String m_sCommandText;
        private int m_iHistoryIndex;
        private int m_iCurrentLine;             //Current line to start printing from
        private int m_iCursorPos;
        private double m_dTimeOpened;           //When was the console opened (Stops the very quick closing)
        private bool m_bJustOpened = false;

        //Console Objects
        private Vector2 m_vConsoleSize;
        private Rectangle[] m_rConsoleDestination;  //Only used if a texture is supplied
        private Rectangle[] m_rConsoleTextureSource;
        private Texture2D m_tBlankTexture;
        private Texture2D m_tTextbox;
        private Texture2D m_tConsole;
        private Rectangle[] m_rTextboxTextureSource;
        private Rectangle[] m_rTextboxDestination;
        private RectangleSprite consoleBackground;
        private SpriteFont m_fFont;
        private KeyboardState m_pKeyboardState;
        private KeyboardState m_pLastKeyboardState;
        private MouseState m_pMouseState;
        private MouseState m_pLastMouseState;

        //Console Variables
        UsageLevel m_eUsageLevel = UsageLevel.NORMAL;
        Color[] m_aTextColours;
        private String m_sConfig;
        private String m_sConfigFolder;
        private const int HISTORY_SIZE = 5;     //Size of the history to keep
        private const int INPUT_MAX = 250;      //Limit the input to 250 Chars 
        private long m_lKeyStrokeSpeed = 115;
        private int m_iConsoleLines;
        private int m_iMaxLines;
        private int m_iFontHeight;
        private double m_lLastKeyPress;
        private bool m_bShowCursor;
        private bool m_bShowBackground;
        private bool m_bOutputToVS;


        //PC Only
#if WINDOWS
        private List<String> m_lFileBuffer;     //This is used only when the m_fFile isn't opened.
        private StreamWriter m_LogWriter;
        private String m_sLog;
#endif

        #region Accessors
        /// <summary>
        /// Singleton used for Parser, doesn't auto create!
        /// </summary>
        protected static Console m_pSingleton;
        public static Console Singleton
        {
            get { return m_pSingleton; }
            set { m_pSingleton = value; }
        }

        /// <summary>
        /// Change the level of the current player, for example if your debugging your game you should set it to DEBUGDEV.
        /// </summary>
        public UsageLevel UserLevel
        {
            get { return m_eUsageLevel; }
            set { m_eUsageLevel = value; }
        }

        public Color BackgroundColour
        {
            get 
            { 
                if (consoleBackground != null)
                    return consoleBackground.Colour;
                else
                    return Color.Black;
            }
            set
            {
                if (consoleBackground != null)
                    consoleBackground.Colour = value;
            }
        }

        /// <summary>
        /// The font that the console will use to write to the screen.
        /// </summary>
        public SpriteFont Font
        {
            get { return m_fFont; }
            set 
            { 
                if (value == null)
                {
                    if (m_pContent != null)
                        m_fFont = m_pContent.Load<SpriteFont>("Courier_New_8pt");
                    else
                        m_fFont = null;
                }
                else
                    m_fFont = value;

                //Resize the font & console
                RestartedDevice();
            }
        }

        /// <summary>
        /// The name of the folder to save the config file to.
        /// </summary>
        public String ConfigFolder
        {
            get { return m_sConfigFolder; }
            set { m_sConfigFolder = value; }
        }

        /// <summary>
        /// The name of the config file to save.
        /// </summary>
        public String ConfigFile
        {
            get { return m_sConfig; }
            set { m_sConfig = value; }
        }

        /// <summary>
        /// The time between keystrokes taken from the keyboard
        /// </summary>
        public long KeyStrokeSpeed
        {
            get { return m_lKeyStrokeSpeed; }
            set { m_lKeyStrokeSpeed = value; }
        }

        /// <summary>
        /// Turn on or off console messages being relayed in visual studio
        /// </summary>
        public bool RelayConsoleToVS
        {
            get { return m_bOutputToVS; }
            set { m_bOutputToVS = value; }
        }

        /// <summary>
        /// Change the default textbox into one of your own choosing
        /// </summary>
        public Texture2D TextboxTexture
        {
            get { return m_tTextbox; }
            set 
            { 
                m_tTextbox = value;

                if (value == null)
                 m_tTextbox = m_pContent.Load<Texture2D>("textbox");

                RestartedDevice();
            }
        }

        /// <summary>
        /// Change the default console into one of your own choosing, setting this to null will 
        /// draw a simple rectangle texture using the background colour
        /// </summary>
        public Texture2D BackgroundTexture
        {
            get { return m_tConsole; }
            set
            {
                m_tConsole = value;

                RestartedDevice();
            }
        }
        #endregion
        
        /// <summary>
        /// Default Constructor
        /// </summary>
        /// <param name="gameMan"></param>
        public Console(Game game, SpriteBatch spriteBatch, Object gameManager) : base(game)
        {
            //Only create if the singleton doesn't exist
            if (m_pSingleton != null)
                return;

            m_pGame = game;
            m_pGameManager = gameManager;
            m_pSpriteBatch = spriteBatch;
            m_lConsoleText = new List<string>();
            m_lHistoryText = new List<string>();
            m_lLineColour = new List<Color>();
            m_dCommands = new Dictionary<string, Command>();
            m_dCVars = new Dictionary<string, CVar>();

            //Setup Default Text Colours
            m_aTextColours = new Color[4];
            m_aTextColours[0] = new Color(255, 0, 0);
            m_aTextColours[1] = new Color(255, 255, 0);
            m_aTextColours[2] = new Color(128, 128, 128);
            m_aTextColours[3] = new Color(255, 255, 255);

#if WINDOWS
            m_lFileBuffer = new List<string>();
#endif

            //Setup Singleton
            m_pSingleton = this;
        }

        /// <summary>
        /// Initialize the console
        /// </summary>
        public override void Initialize()
        {
#if WINDOWS
            m_sLog = "Debug.log";
#endif
            m_sConfig = "CVars.conf";
            m_sConfigFolder = "OpiConsole";
            m_sCommandText = "";
            m_iHistoryIndex = 0;
            m_iCursorPos = 0;
            m_iCurrentLine = 0;
            m_iConsoleLines = 10;
            m_iMaxLines = 250;
            m_bShowBackground = true;
            m_bShowCursor = true;

            m_pSpriteBatch = new SpriteBatch(m_pGame.GraphicsDevice);
            m_pContent = new ResourceContentManager(m_pGame.Services, Resources.ResourceManager);

            //Load Resources
#if WINDOWS
            m_fFont = m_pContent.Load<SpriteFont>("Courier_New_8pt");
            m_tBlankTexture = m_pContent.Load<Texture2D>("blank");
            m_tTextbox = m_pContent.Load<Texture2D>("textbox");
            m_tConsole = m_pContent.Load<Texture2D>("console");
#else
            m_fFont = m_pContent.Load<SpriteFont>("Courier_New_8pt_xbox");
            m_tBlankTexture = m_pContent.Load<Texture2D>("blank_xbox");
            m_tTextbox = m_pContent.Load<Texture2D>("textbox_xbox");
#endif

            //Font Config
            m_fFont.Spacing = 0;
            m_iFontHeight = (int)m_fFont.MeasureString("A").Y;

            m_vConsoleSize = new Vector2(m_pGame.GraphicsDevice.Viewport.Width, m_iFontHeight * m_iConsoleLines);
            consoleBackground = new RectangleSprite(0, 0, 300, 300, m_tBlankTexture);
            consoleBackground.Colour = new Color(0, 0, 0, 100);

            //Size Textbox Source & Destinations (Idea From: xWinForms)
            m_rTextboxTextureSource = new Rectangle[3];
            m_rTextboxDestination = new Rectangle[3];

            m_rTextboxTextureSource[0] = new Rectangle(0, 0, m_tTextbox.Width - 1, m_tTextbox.Height);
            m_rTextboxTextureSource[1] = new Rectangle(m_tTextbox.Width - 1, 0, 1, m_tTextbox.Height);
            m_rTextboxTextureSource[2] = new Rectangle(m_tTextbox.Width - 1, 0, -(m_tTextbox.Width - 1), m_tTextbox.Height);

            int tmpYPos = m_iConsoleLines * m_iFontHeight;
            int midSize = (int)m_vConsoleSize.X - (m_tTextbox.Width - 1);
            m_rTextboxDestination[0] = new Rectangle(0, tmpYPos, m_tTextbox.Width - 1, m_iFontHeight);
            m_rTextboxDestination[1] = new Rectangle(m_tTextbox.Width - 1, tmpYPos, midSize - (m_tTextbox.Width - 1), m_iFontHeight);
            m_rTextboxDestination[2] = new Rectangle(midSize, tmpYPos, m_tTextbox.Width - 1, m_iFontHeight);
            
            //If console texture exists
            if (m_tConsole != null)
            {
                m_rConsoleTextureSource = new Rectangle[3];
                m_rConsoleDestination = new Rectangle[3];

                m_rConsoleTextureSource[0] = new Rectangle(0, 0, m_tConsole.Width - 1, m_tConsole.Height);
                m_rConsoleTextureSource[1] = new Rectangle(m_tConsole.Width - 1, 0, 1, m_tConsole.Height);
                m_rConsoleTextureSource[2] = new Rectangle(m_tConsole.Width - 1, 0, -(m_tConsole.Width - 1), m_tConsole.Height);
                m_rConsoleDestination[0] = new Rectangle(0, 0, m_tConsole.Width - 1, tmpYPos);
                m_rConsoleDestination[1] = new Rectangle(m_tConsole.Width - 1, 0, midSize - (m_tConsole.Width - 1), tmpYPos);
                m_rConsoleDestination[2] = new Rectangle(midSize, 0, m_tConsole.Width - 1, tmpYPos);
            }

#if WINDOWS
            try
            {
                File.Delete(m_sLog);
                m_LogWriter = new StreamWriter(m_sLog);
                m_LogWriter.AutoFlush = true;
            }
            catch (System.IO.IOException)
            {
            	//Do Nothing for now
            }
#endif

            AddDefaultCommands();
            AddDefaultCVars();

            Print("--- Intialised Console ---", ConsoleLevel.DEBUG);

            m_pGame.GraphicsDevice.Reset();

            base.Initialize();
        }

        /// <summary>
        /// Call this when your device has been restarted to update the size of the console.
        /// </summary>
        public void RestartedDevice()
        {
            m_fFont.Spacing = 0;
            m_iFontHeight = (int)m_fFont.MeasureString("A").Y;
            m_vConsoleSize = new Vector2(m_pGame.GraphicsDevice.Viewport.Width, m_iFontHeight * m_iConsoleLines);

            int tmpYPos = m_iConsoleLines * m_iFontHeight;
            int midSize = (int)m_vConsoleSize.X - (m_tTextbox.Width - 1);
            m_rTextboxDestination[0] = new Rectangle(0, tmpYPos, m_tTextbox.Width - 1, m_iFontHeight);
            m_rTextboxDestination[1] = new Rectangle(m_tTextbox.Width - 1, tmpYPos, midSize - (m_tTextbox.Width -1), m_iFontHeight);
            m_rTextboxDestination[2] = new Rectangle(midSize, tmpYPos, m_tTextbox.Width - 1, m_iFontHeight);

            //If console texture exists
            if (m_tConsole != null)
            {
                m_rConsoleDestination[0] = new Rectangle(0, 0, m_tConsole.Width - 1, tmpYPos);
                m_rConsoleDestination[1] = new Rectangle(m_tConsole.Width - 1, 0, midSize - (m_tConsole.Width - 1), tmpYPos);
                m_rConsoleDestination[2] = new Rectangle(midSize, 0, m_tConsole.Width - 1, tmpYPos);
            }
        }


        /// <summary>
        /// Print a normal message to the console
        /// </summary>
        /// <param name="str">The message to print</param>
        public void Print(String str)
        {
            Print(str, ConsoleLevel.NORM);
        }

        /// <summary>
        /// Print a message to the console or just the textfile according to the ConsoleLevel
        /// </summary>
        /// <param name="str">The message to print</param>
        /// <param name="level">The level of the message (indicates who and where the message is shown)</param>
        public void Print(String str, ConsoleLevel level)
        {
            //If we don't want to show debug just discard it
            if (level == ConsoleLevel.DEBUG && UserLevel != UsageLevel.DEBUGDEV)
                return;

            //If debug is active send all prints to the textfile
            if (UserLevel == UsageLevel.DEBUGDEV)
            {
#if WINDOWS
                if (m_LogWriter == null)
                {
                    m_lFileBuffer.Add(GetTime() + " " + str);
                    m_lConsoleText.Add(str);
                    m_lLineColour.Add(GetColour(level));
                    return;
                }
                else
                {
                    //Copies all the buffer to the file
                    if (m_lFileBuffer.Count > 0)
                    {
                        foreach (String tmpStr in m_lFileBuffer)
                            m_LogWriter.WriteLine(tmpStr);

                        m_lFileBuffer.Clear();
                    }

                    m_LogWriter.WriteLine(GetTime() + " " + str);
                }
#endif
            }

            //Textfile printing doesn't need to be added to the console
            if (level == ConsoleLevel.TXTFILE)
                return;

            //Too Long for the line
            if (m_fFont.MeasureString(str).X > m_vConsoleSize.X)
            {
                //Split the line up
                List<string> split = SplitString(str);

                foreach (string splitStr in split)
                {
                    m_lConsoleText.Add(splitStr);
                    m_lLineColour.Add(GetColour(level));
                }
            }
            //Line is fine as it is just add it
            else
            {
                m_lConsoleText.Add(str);
                m_lLineColour.Add(GetColour(level));

                if (m_bOutputToVS)
                {
                    if (level == ConsoleLevel.FAIL)
                        System.Console.Error.WriteLine(str);
                    else
                        System.Console.WriteLine(str);
                }
            }
            ResizeMessages();
        }

        /// <summary>
        /// Split strings longer than the console window so that strings won't 
        /// be drawn off the screen
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private List<string> SplitString(string str)
        {
            List<string> output = new List<string>();
            int maxChars = (int)m_vConsoleSize.X / (int)m_fFont.MeasureString("a").X;   

            while (m_fFont.MeasureString(str).X > m_vConsoleSize.X)
            {
                //Check if theres a word at the split point
                char cBefore = str[maxChars-1];
                char cAfter = str[maxChars+1];

                if (char.IsLetterOrDigit(cBefore) && char.IsLetterOrDigit(cAfter))
                {
                    //Check how long the word/number is by looking for the nearest space
                    int pos=-1;
                    int fSpace = -2;
                    while (pos < maxChars-1 && pos != fSpace)
                    {
                        fSpace = pos;
                        pos = str.IndexOf(' ', pos+1);
                    }
                    int lSpace = str.IndexOf(' ', maxChars + 1);

                    //Word is too long we'll just have to cope with a broken word
                    if (lSpace - fSpace > maxChars)
                    {
                        string tmpString = str.Substring(0, maxChars);
                        output.Add(tmpString);
                        str = str.Remove(0, maxChars);
                    }

                    //Split the string at the beginning of the word
                    else
                    {
                        string tmpString = str.Substring(0, fSpace);
                        output.Add(tmpString);
                        str = str.Remove(0, fSpace);
                    }
                }

                //Not a word/number so just split or its too long
                else
                {
                    string tmpString = str.Substring(0, maxChars);
                    output.Add(tmpString);
                    str = str.Remove(0, maxChars);
                }

            }
            output.Add(str);
            return output;
        }

        /// <summary>
        /// Outputs the current time, would like to move this to a GameTime but don't know how to extract the 
        /// GameTime from a Game component
        /// </summary>
        /// <returns></returns>
        private String GetTime()
        {
            DateTime dt = DateTime.Now;

            return dt.Hour + ":" +
                dt.Minute + ":" +
                dt.Second + ":" +
                dt.Millisecond;
        }

        /// <summary>
        /// Handle command inputs, this is called when the return key is pressed
        /// </summary>
        /// <param name="cmd"></param>
        void HandleCommand(String cmd)
        {
            String output = "User >> " + cmd;
            Print(output, ConsoleLevel.TXTFILE);

            //Check to see if it parses
            CommandParser cmdParser = new CommandParser(cmd);
            String commandName;
            List<String> args;

            if (cmdParser.Parse(out commandName, out args))
            {
                if (!Dispatch(commandName, args))
                    return;
            }
            else
            {
                output = "Command " + commandName + " has incorrect parameters.";
                Print(output, ConsoleLevel.WARN);
            }
        }

        /// <summary>
        /// Clear the console of all lines
        /// </summary>
        public void Clear()
        {
            m_lConsoleText.Clear();
            m_lLineColour.Clear();
        }

        /// <summary>
        /// Removes lines that won't be stored any longer
        /// </summary>
        private void ResizeMessages()
        {
            int lineHeight = 0;
            int fontHeight = 0;
            if (CalculateLimits(out lineHeight, out fontHeight))
            {
                int messageCount = m_lConsoleText.Count;
                if (messageCount > m_iMaxLines)
                {
                    m_lConsoleText.RemoveRange(0, messageCount - m_iMaxLines);
                    m_lLineColour.RemoveRange(0, messageCount - m_iMaxLines);
                }
            }

        }

        /// <summary>
        /// Calculates the size of the fonts height and line size
        /// </summary>
        /// <param name="lineHeight"></param>
        /// <param name="fontHeight"></param>
        /// <returns></returns>
        private bool CalculateLimits(out int lineHeight, out int fontHeight)
        {
            if (m_fFont != null)
            {
                fontHeight = (int)m_fFont.MeasureString("a").Y;
                lineHeight = m_fFont.LineSpacing;

                return true;
            }

            lineHeight = 0;
            fontHeight = 0;
            return false;
        }

        /// <summary>
        /// Adds an input to the history
        /// </summary>
        /// <param name="str"></param>
        private void AddToHistory(String str)
        {
            if (m_lHistoryText.Count >= HISTORY_SIZE)
            {
                m_lHistoryText.RemoveAt(0);
            }

            m_lHistoryText.Add(str);
        }

        /// <summary>
        /// Gets the colour matched with the console level given
        /// </summary>
        /// <param name="level"></param>
        /// <returns></returns>
        private Color GetColour(ConsoleLevel level)
        {
            switch (level)
            {
                case ConsoleLevel.FAIL:
                    return m_aTextColours[0];
                case ConsoleLevel.WARN:
                    return m_aTextColours[1];
                case ConsoleLevel.DEBUG:
                    return m_aTextColours[2];
                default:
                    return m_aTextColours[3];
            }
        }

        /// <summary>
        /// Sets the colour matched with the console level given
        /// </summary>
        /// <param name="level"></param>
        /// <returns></returns>
        public void SetTextColour(Color colour, ConsoleLevel level)
        {
            //Refresh Colours
            for (int i = 0; i < m_lLineColour.Count; i++)
            {
                switch (level)
                {
                    case ConsoleLevel.FAIL:
                        if (m_lLineColour[i] == m_aTextColours[0])
                            m_lLineColour[i] = colour;
                        break;
                    case ConsoleLevel.WARN:
                        if (m_lLineColour[i] == m_aTextColours[1])
                            m_lLineColour[i] = colour;
                        break;
                    case ConsoleLevel.DEBUG:
                        if (m_lLineColour[i] == m_aTextColours[2])
                            m_lLineColour[i] = colour;
                        break;
                    case ConsoleLevel.NORM:
                        if (m_lLineColour[i] == m_aTextColours[3])
                            m_lLineColour[i] = colour;
                        break;
                }
            }

            //Set new colour
            switch (level)
            {
                case ConsoleLevel.FAIL:
                    m_aTextColours[0] = colour;
                    break;
                case ConsoleLevel.WARN:
                    m_aTextColours[1] = colour;
                    break;
                case ConsoleLevel.DEBUG:
                    m_aTextColours[2] = colour;
                    break;
                case ConsoleLevel.NORM:
                    m_aTextColours[3] = colour;
                    break;
            }
        }

        /// <summary>
        /// Updates the input for the console
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            if (!this.Enabled)
                return;

            //Check key input
            m_pKeyboardState = Keyboard.GetState();
            Keys[] keys = m_pKeyboardState.GetPressedKeys();

            //This allows us to use the Oem8 Key as a close and open without it closing in an instant
            if (m_dTimeOpened == 0)
                m_dTimeOpened = gameTime.TotalGameTime.TotalMilliseconds;
            else if (m_bJustOpened && gameTime.TotalGameTime.TotalMilliseconds - m_dTimeOpened > m_lKeyStrokeSpeed)
                m_bJustOpened = false;

            double lastPress = gameTime.TotalGameTime.TotalMilliseconds - m_lLastKeyPress;
            foreach (Keys key in keys)
            {
                if (m_pLastKeyboardState.IsKeyUp(key) || lastPress > m_lKeyStrokeSpeed)
                {
                    HandleKeyPress(key);
                    UpdateKeyboard(gameTime);
                }
            }

            //If were still not drawing the console lets exit
            if (!DrawConsole)
                return;

            consoleBackground.Size = m_vConsoleSize;

            //Mouse Scroll Wheel
            m_pMouseState = Mouse.GetState();
            int wheelDiff = m_pLastMouseState.ScrollWheelValue - m_pMouseState.ScrollWheelValue;
            if (wheelDiff < 0)
            {
                m_iCurrentLine -= -wheelDiff / 120;
                if (m_iCurrentLine < m_iConsoleLines - m_lConsoleText.Count)
                    m_iCurrentLine = m_iConsoleLines - m_lConsoleText.Count;

                if (m_iCurrentLine > 0)
                    m_iCurrentLine = 0;
            }
            if (wheelDiff > 0)
            {
                m_iCurrentLine += wheelDiff / 120;
                if (m_iCurrentLine > 0)
                    m_iCurrentLine = 0;
            }
            m_pLastMouseState = m_pMouseState;
        }

        /// <summary>
        /// Updates the keyboards last input
        /// </summary>
        /// <param name="gameTime"></param>
        void UpdateKeyboard(GameTime gameTime)
        {
            m_lLastKeyPress = gameTime.TotalGameTime.TotalMilliseconds;
            m_pLastKeyboardState = m_pKeyboardState;
        }

        /// <summary>
        /// Handles any key press that occurs
        /// </summary>
        /// <param name="key"></param>
        void HandleKeyPress(Keys key)
        {
            //Open Console
            if (key == Keys.Oem8 && !DrawConsole)
            {
                DrawConsole = true;
            }

            //Console Not Open Don't take input
            if (!DrawConsole)
                return;

            //Close Console
            if (key == Keys.Escape || key == Keys.Oem8 && !m_bJustOpened)
            {
                DrawConsole = false;
            }

            //Handle Command Input (Return)
            else if (key == Keys.Enter)
            {
                if (m_sCommandText.Length > 0)
                {
                    AddToHistory(m_sCommandText);
                    HandleCommand(m_sCommandText);
                    m_sCommandText = "";
                    m_iCursorPos = 0;
                    m_iHistoryIndex = 0;
                }
            }
            else if (key == Keys.Back)
            {
                if (m_iCursorPos > 0)
                {
                    m_sCommandText = m_sCommandText.Remove(m_iCursorPos - 1, 1);
                    m_iCursorPos--;
                }
            }
            else if (key == Keys.Delete)
            {
                //Delete a char
                if (m_sCommandText.Length > m_iCursorPos)
                    m_sCommandText = m_sCommandText.Remove(m_iCursorPos, 1);
            }
            else if (key == Keys.Left && m_pKeyboardState.IsKeyDown(Keys.LeftShift))
            {
                m_iCursorPos = 0;
            }
            else if (key == Keys.Left)
            {
                if (--m_iCursorPos < 0)
                    m_iCursorPos = 0;
            }
            else if (key == Keys.Right && m_pKeyboardState.IsKeyDown(Keys.LeftShift))
            {
                m_iCursorPos = m_sCommandText.Length;
            }
            else if (key == Keys.Right)
            {
                if (++m_iCursorPos > m_sCommandText.Length)
                    m_iCursorPos = m_sCommandText.Length;
            }
            else if (key == Keys.Home)
            {
                m_iCursorPos = 0;
            }
            else if (key == Keys.End)
            {
                m_iCursorPos = m_sCommandText.Length;
            }
            else if (key == Keys.Down)
            {

                if (m_lHistoryText.Count > 0)
                {

                    int index = m_iHistoryIndex + 1;
                    if (index >= 0 && index < m_lHistoryText.Count)
                    {
                        m_iHistoryIndex++;
                    }
                    else
                    {
                        m_iHistoryIndex = 0;
                    }
                }
                else
                {
                    m_iHistoryIndex = 0;
                }

                if (m_lHistoryText.Count != 0)
                {
                    m_sCommandText = m_lHistoryText[m_iHistoryIndex];
                    m_iCursorPos = m_sCommandText.Length;
                }
            }
            else if (key == Keys.Up)
            {
                if (m_lHistoryText.Count > 0)
                {

                    int index = m_iHistoryIndex - 1;
                    if (index >= 0 && index < m_lHistoryText.Count)
                    {
                        m_iHistoryIndex--;
                    }
                    else
                    {
                        m_iHistoryIndex = m_lHistoryText.Count - 1;
                    }
                }
                else
                {
                    m_iHistoryIndex = 0;
                }

                if (m_lHistoryText.Count != 0)
                {
                    m_sCommandText = m_lHistoryText[m_iHistoryIndex];
                    m_iCursorPos = m_sCommandText.Length;
                }
            }
            else if (key == Keys.Tab)
            {
                AutoComplete();
            }
            else if (key == Keys.PageUp)
            {
                m_iCurrentLine--;
                if (m_iCurrentLine < m_iConsoleLines - m_lConsoleText.Count)
                    m_iCurrentLine = m_iConsoleLines - m_lConsoleText.Count;

                if (m_iCurrentLine > 0)
                    m_iCurrentLine = 0;
            }
            else if (key == Keys.PageDown)
            {
                m_iCurrentLine++;
                if (m_iCurrentLine > 0)
                    m_iCurrentLine = 0;
            }
#if WINDOWS
            else if (key == Keys.Insert || key == Keys.V && m_pKeyboardState.IsKeyDown(Keys.LeftControl) ||
                    key == Keys.V && m_pKeyboardState.IsKeyDown(Keys.RightControl))
            {

                Thread t = new Thread(Clipboard);
                t.SetApartmentState(ApartmentState.STA);
                t.Start();
            }
#endif
            else
            {
                //Hack to find new keys
                //Print(key.ToString(), ConsoleLevel.NORM);

                for (int i = 0; i < m_sAInput.Length; i++)
                {
                    if (key.ToString() == m_sAInput[i].input)
                    {
                        if (m_pKeyboardState.IsKeyDown(Keys.RightShift) ||
                            m_pKeyboardState.IsKeyDown(Keys.LeftShift))
                        {
                            if (m_iCursorPos == 0)
                                m_sCommandText += m_sAInput[i].shiftOutput;
                            else
                                m_sCommandText = m_sCommandText.Insert(m_iCursorPos, m_sAInput[i].shiftOutput);
                        }
                        else
                        {
                            if (m_iCursorPos == 0)
                                m_sCommandText += m_sAInput[i].output;
                            else
                                m_sCommandText = m_sCommandText.Insert(m_iCursorPos, m_sAInput[i].output);
                        }

                        m_iCursorPos++;
                        break;
                    }
                }

            }

            //Check to see if the message is too long and trim it
            if (m_sCommandText.Length > INPUT_MAX)
            {
                m_sCommandText = m_sCommandText.Substring(0, INPUT_MAX);
                if (m_iCursorPos > m_sCommandText.Length)
                    m_iCursorPos = m_sCommandText.Length;
            }
        }

#if WINDOWS
        /// <summary>
        /// Grabs the text from the clipboard in a STAThread
        /// </summary>
        private void Clipboard()
        {
            if (System.Windows.Forms.Clipboard.ContainsText())
            {
                m_sCommandText += System.Windows.Forms.Clipboard.GetText();
                m_iCursorPos += System.Windows.Forms.Clipboard.GetText().Length;
            }
        }
#endif

        /// <summary>
        /// Autocompletes a command if one is found, if there is more than
        /// one command found then a list of commands will be printed,
        /// and if all of those commands begin with the same characters
        /// then those characters will be auto completed.
        /// </summary>
        public void AutoComplete()
        {
            //We don't have anything to work with return
            if (m_sCommandText.Length == 0)
                return;

            List<String> cmdNames;
            String tmpCommand = m_sCommandText;
            bool cvar = false;
            bool help = false;
            m_sCommandText.ToLower();

            //If we are tabCompleting with cvar
            if (m_sCommandText.Length > 5 && m_sCommandText.Substring(0, 5) == "cvar ")
            {
                tmpCommand = m_sCommandText.Substring(5);
                m_sCommandText.ToLower();
                cmdNames = GetCVars();
                cvar = true;
            }

            //If we are tabCompleting with help
            else if (m_sCommandText.Length > 5 && m_sCommandText.Substring(0, 5) == "help ")
            {
                tmpCommand = m_sCommandText.Substring(5);
                m_sCommandText.ToLower();
                cmdNames = GetCommands();
                help = true;
            }

            //All other commands
            else              
                cmdNames = GetCommands();

            List<String> output = new List<string>();

            foreach (String str in cmdNames)
            {
                if (str.Length == tmpCommand.Length)
                {
                    //We already have the command!
                    if (str.ToLower() == tmpCommand.ToLower())
                        return;
                }
                else if (tmpCommand.Length < str.Length)
                {
                    String tmpStr;
                    tmpStr = str;
                    if (str.Substring(0, tmpCommand.Length).ToLower() == tmpCommand.ToLower())
                    {
                        //If were doing a cvar
                        if (cvar)
                        {
                            if (GetCVar(str).UsageLevel < m_eUsageLevel)
                                continue;

                            tmpStr = "cvar " + str;
                        }
                        //If were doing help
                        else if (help)
                        {
                            if (GetCommand(str).UsageLevel < m_eUsageLevel)
                                continue;

                            tmpStr = "help " + str;
                        }
                        else
                        {
                            if (GetCommand(str).UsageLevel < m_eUsageLevel)
                                continue;
                        }
                        output.Add(tmpStr);
                    }
                }
            }

            //We found none
            if (output.Count == 0)
                return;

            //If we have just one complete the command
            if (output.Count == 1)
            {
                m_sCommandText = output[0] + " ";
                m_iCursorPos = m_sCommandText.Length;
                return;
            }
                
            //Sort Alphabetically
            output.Sort();

            //Check for the smallest sized choice we have
            int smallestSize = 9999;
            for (int i = 0; i < output.Count; i++)
            {
                if (output[i].Length < smallestSize)
                    smallestSize = output[i].Length;
            }

            //Now we find if the start substring of all the commands are the same
            String subStr = "";
            for (int i = 0; i < smallestSize; i++)
            {
                bool charDifferent = false;
                for (int y = 0; y < output.Count; y++)
                {
                    if ((output[0][i] != output[y][i]))
                    {
                        charDifferent = true;
                        break;
                    }
                }
                if (charDifferent)
                    break;
                else
                    subStr += output[0][i];
            }

            //Adds the substring found in all the commands
            if (subStr.Length != m_sCommandText.Length)
            {
                m_sCommandText = subStr;
                m_iCursorPos = m_sCommandText.Length;
            }
            else
            {
                //Otherwise show the list of commands
                String msg = "Available Commands: ";
                foreach (String str in output)
                    msg += str + ",";
                msg = msg.Remove(msg.Length -1, 1);
                Print(msg, ConsoleLevel.NORM);
            }
        }

        /// <summary>
        /// Draw the console
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Draw(GameTime gameTime)
        {
            if (!DrawConsole)
                return;

            m_pSpriteBatch.Begin();

            //Draw the background
            if (m_bShowBackground)
            {
                //If console texture draw the texture
                if (m_tConsole != null)
                {
                    m_pSpriteBatch.Draw(m_tConsole, m_rConsoleDestination[0], m_rConsoleTextureSource[0], Color.White);
                    m_pSpriteBatch.Draw(m_tConsole, m_rConsoleDestination[1], m_rConsoleTextureSource[1], Color.White);
                    m_pSpriteBatch.Draw(m_tConsole, m_rConsoleDestination[2], m_rConsoleTextureSource[2], Color.White);
                }
                else
                    consoleBackground.DrawElement(gameTime, m_pSpriteBatch);
            }

            //Used to draw the lines in the correct place
            Vector2 tmpPos = new Vector2(m_tTextbox.Width + 1,0);

        	//Get first & last line (Starts drawing with the right line)
            int fLine = 0;
            int	lLine = m_lConsoleText.Count;
            if (m_lConsoleText.Count > m_iConsoleLines)
            {
	            fLine = (m_iCurrentLine - m_iConsoleLines) + m_lConsoleText.Count;
	            lLine = fLine + m_iConsoleLines;
            }

            //Draw the console Text
            for (int i = fLine; i < lLine; i++)
            {
                m_pSpriteBatch.DrawString(m_fFont, m_lConsoleText[i], tmpPos, m_lLineColour[i]);
                tmpPos.Y += m_iFontHeight;
            }

            //Draw the Textbox
            m_pSpriteBatch.Draw(m_tTextbox, m_rTextboxDestination[0], m_rTextboxTextureSource[0], Color.White);
            m_pSpriteBatch.Draw(m_tTextbox, m_rTextboxDestination[1], m_rTextboxTextureSource[1], Color.White);
            m_pSpriteBatch.Draw(m_tTextbox, m_rTextboxDestination[2], m_rTextboxTextureSource[2], Color.White);

            //Draw the input text
            int charWidth = (int)m_fFont.MeasureString("a").X;
            tmpPos = new Vector2(charWidth, m_iConsoleLines * m_iFontHeight);
            String tmpOutput;

            if (m_iCursorPos - (m_vConsoleSize.X / charWidth) < 0)
                tmpOutput = m_sCommandText;
            else
                tmpOutput = m_sCommandText.Substring(m_iCursorPos - (int)(m_vConsoleSize.X / charWidth));

            if (gameTime.TotalGameTime.Ticks % 20 == 0)
                m_bShowCursor = !m_bShowCursor;

            try
            {
                m_pSpriteBatch.DrawString(m_fFont, tmpOutput, tmpPos, m_aTextColours[3]);
            }
            catch (System.ArgumentException)
            {
                //An invalid character only happens if you copy + paste something in atm.
            }

            //Draw Cursor
            if (m_bShowCursor)
            {
                tmpPos.X = charWidth * (m_iCursorPos + 1) - 3;
                m_pSpriteBatch.DrawString(m_fFont, "|", tmpPos, m_aTextColours[3]);
            }

            m_pSpriteBatch.End();

            base.Draw(gameTime);
        }

        /// <summary>
        /// Dispose of the console
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
#if WINDOWS
            if (disposing)
            {
                m_LogWriter.Close();
            }
#endif
            base.Dispose(disposing);
        }

        /// <summary>
        /// Should the console be drawn?
        /// </summary>
        public bool DrawConsole
        {
            get { return this.Visible; }
            set 
            {
                if (value)
                {
                    m_bJustOpened = true;
                    m_dTimeOpened = 0;
                }
                this.Visible = value; 
            }
        }

        //*==========================*
        //*=    Start of Commands   =*
        //*==========================*

        /// <summary>
        /// Add the default commands to the console
        /// </summary>
        private void AddDefaultCommands()
        {
            Command cmd = new Command_Init(m_pGame, this, m_pGameManager);

            cmd = new Command_CVar();
            RegisterCommand(cmd);
            cmd = new Command_CLS();
            RegisterCommand(cmd);
            cmd = new Command_Quit();
            RegisterCommand(cmd);
            cmd = new Command_Help();
            RegisterCommand(cmd);
            cmd = new Command_ListCommands();
            RegisterCommand(cmd);
            cmd = new Command_SaveConfig();
            RegisterCommand(cmd);
            cmd = new Command_LoadConfig();
            RegisterCommand(cmd);
        }

        /// <summary>
        /// Register a command to the console if it doesn't already exist
        /// </summary>
        /// <param name="cmd"></param>
        /// <returns></returns>
        public bool RegisterCommand(Command cmd)
        {
            //If the command doesn't exist
            if (!HasCommand(cmd.Name))
            {
                m_dCommands[cmd.Name] = cmd;
                return true;
            }

            return false;
        }

        /// <summary>
        /// Does the console have this command?
        /// </summary>
        /// <param name="cmd"></param>
        /// <returns></returns>
        private bool HasCommand(String cmd)
        {
            if (m_dCommands.ContainsKey(cmd))
                return true;

            return false;
        }

        /// <summary>
        /// Run the command that has been supplied
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        protected bool Dispatch(String cmd, List<String> args)
        {
            if (m_dCommands.ContainsKey(cmd))
            {
                Command tmpCommand = m_dCommands[cmd];
                if (tmpCommand.UsageLevel < m_eUsageLevel)
                {
                    Print("Don't have the required permission to use the command: " + cmd, ConsoleLevel.WARN);
                    return false;
                }
                if (!tmpCommand.Invoke(args))
                {
                    if (tmpCommand.Name == "cvar" && args.Count > 0)
                    {
                        //Show CVar Not Found
                        String output = "CVar " + args[0] + " not found.";
                        Print(output, ConsoleLevel.WARN);
                        return false;
                    }

                    //Show Help
                    String outs = "Command " + cmd + " usage: " + tmpCommand.Usage;
                    Print(outs, ConsoleLevel.WARN);
                    return false;
                }
            }
            else
            {
                String output = "Command " + cmd + " doesn't exist.";
                Print(output, ConsoleLevel.WARN);
                return false;
            }

            return true;
        }

        /// <summary>
        /// Returns a command if it exists
        /// </summary>
        /// <param name="cmdName"></param>
        /// <returns></returns>
        public Command GetCommand(String cmdName)
        {
            Command tmpCommand;
            if (!m_dCommands.TryGetValue(cmdName, out tmpCommand))
                return null;
            else
                return tmpCommand;
        }

        /// <summary>
        /// Returns all commands added to the console
        /// </summary>
        /// <returns></returns>
        public List<String> GetCommands()
        {
            List<string> output = new List<String>();
 
            foreach (KeyValuePair<String, Command> keyPair in m_dCommands)
            {
                output.Add(keyPair.Key);
            }

            return output;
        }

        //*==========================*
        //*=    End of Commands     =*
        //*==========================*


        //*==========================*
        //*=    Start of CVars      =*
        //*==========================*

        /// <summary>
        /// Add default CVars to the console
        /// </summary>
        private void AddDefaultCVars()
        {
            CVar cvar = new CVar_Init(m_pGame);

            //TODO: Add Default CVar
        }

        /// <summary>
        /// Register a CVar with the console
        /// </summary>
        /// <param name="cvar"></param>
        /// <returns></returns>
        public bool RegisterCVar(CVar cvar)
        {
            //If the command doesn't exist
            if (!HasCommand(cvar.Name))
            {
                m_dCVars[cvar.Name] = cvar;
                return true;
            }

            return false;
        }

        /// <summary>
        /// Does the console have this CVar?
        /// </summary>
        /// <param name="cvar"></param>
        /// <returns></returns>
        public bool HasCVar(String cvar)
        {
            if (m_dCVars.ContainsKey(cvar))
                return true;

            return false;
        }

        /// <summary>
        /// Set the current value of a CVar, requires a list of args.
        /// </summary>
        /// <param name="cvarName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public bool setCVar(String cvarName, List<String> args)
        {
            CVar tmpCVar;

            if (!m_dCVars.TryGetValue(cvarName, out tmpCVar))
                return false;

            if (tmpCVar != null)
            {
                if (tmpCVar.UsageLevel < m_eUsageLevel)
                {
                    Print("Don't have the required permission to use the CVar: " + cvarName, ConsoleLevel.WARN);
                    return false;
                }

                //If failure
                if (!tmpCVar.Invoke(args))
                {
                    String output = "CVar " + cvarName;

                    if (args.Count == 0)
                    {
                        output += " value: " + tmpCVar.ToString();
                    }

                    output += " usage: " + tmpCVar.getUsage();
                    output += " (default: " + tmpCVar.DefaultValue + ")";
                    Print(output, ConsoleLevel.WARN);

                    return false;
                }
            }
            else
            {
                Print("CVar " + cvarName + " doesn't exist.", ConsoleLevel.WARN);
                return false;
            }

            return true;
        }

        /// <summary>
        /// Set the current value of a CVar, this will also parse the 
        /// arguments from a string
        /// </summary>
        /// <param name="cvarName"></param>
        /// <param name="strArgs"></param>
        /// <returns></returns>
        public bool setCVar(String cvarName, String strArgs)
        {
            CommandParser parser = new CommandParser(cvarName + " " + strArgs);
            List<String> args;

            if (!parser.Parse(out cvarName, out args))
            {
                return false;
            }

            CVar tmpCVar;

            if (!m_dCVars.TryGetValue(cvarName, out tmpCVar))
                return false;

            if (tmpCVar != null)
            {
                //If failure
                if (!tmpCVar.Invoke(args))
                {
                    String output = "CVar " + cvarName;

                    if (args.Count == 0)
                    {
                        output += " value: " + tmpCVar.ToString();
                    }

                    output += " usage: " + tmpCVar.getUsage();
                    output += " (default: " + tmpCVar.DefaultValue + ")";
                    Print(output, ConsoleLevel.WARN);

                    return false;
                }
            }
            else
            {
                Print("CVar " + cvarName + " doesn't exist.", ConsoleLevel.WARN);
                return false;
            }

            return true;
        }

        /// <summary>
        /// Returns a CVar if it exists
        /// </summary>
        /// <param name="cvarName"></param>
        /// <returns></returns>
        public CVar GetCVar(String cvarName)
        {
            CVar tmpCVar;
            if (!m_dCVars.TryGetValue(cvarName, out tmpCVar))
                return null;
            else
                return tmpCVar;
        }

        /// <summary>
        /// Get all CVars registered with the console
        /// </summary>
        /// <returns></returns>
        public List<String> GetCVars()
        {
            List<String> output = new List<string>();

            foreach (KeyValuePair<String, CVar> keyPair in m_dCVars)
                output.Add(keyPair.Key);

            return output;
        }

        /// <summary>
        /// Get all the CVars that are to be saved to an external file
        /// </summary>
        /// <returns></returns>
        public List<CVar> GetSaveCVars()
        {
            List<CVar> output = new List<CVar>();

            foreach (KeyValuePair<String, CVar> keyPair in m_dCVars)
            {
                if (keyPair.Value.SaveToFile)
                    output.Add(keyPair.Value);
            }

            return output;
        }

        //*==========================*
        //*=      End of CVars      =*
        //*==========================*


        //*==========================*
        //*=    Start of Config     =*
        //*==========================*

        private StorageContainer GetContainer()
        {
            if (m_pContainer == null)
            {
                //Setup Container
                IAsyncResult result = Guide.BeginShowStorageDeviceSelector(null, null);
                StorageDevice device = Guide.EndShowStorageDeviceSelector(result);
                m_pContainer = device.OpenContainer(m_sConfigFolder);
            }

            return m_pContainer;
        }

        /// <summary>
        /// Load the saved cvars in the config file.
        /// </summary>
        public void LoadConfig()
        {
            //Load the XML Config File
            Config config = new Config();

            FileStream fs = null;
            try
            {
                //Grab Container
                StorageContainer container = GetContainer(); 

                fs = new FileStream(Path.Combine(container.Path, m_sConfig), FileMode.Open, FileAccess.Read);
                XmlSerializer xs = new XmlSerializer(config.GetType());
                config = (Config)xs.Deserialize(fs);
                fs.Close();

                //Import the CVars
                foreach (DictionaryEntry de in config.m_lCvars)
                {
                    CVar c = GetCVar((String)de.Key);
                    if (c != null)
                        setCVar((String)de.Key, (String)de.Value);
                }

                Print("--- Loaded Config ---", ConsoleLevel.DEBUG);
            }
            catch(FileNotFoundException)
            {
                //Do Nothing we just don't have any saved settings
                Print("--- No Config File Found ---", ConsoleLevel.DEBUG);
                if (fs != null)
                    fs.Close();
            }



        }

        /// <summary>
        /// Save the cvar config file.
        /// </summary>
        public void SaveConfig()
        {
            //Get the cvars
            int i = 0;
            List<CVar> cvars = GetSaveCVars();
            DictionaryEntry[] d_Cvars;
            d_Cvars = new DictionaryEntry[cvars.Count];

            foreach (CVar cvar in cvars)
            {
                DictionaryEntry de = new DictionaryEntry();
                de.Value = cvar.ToString();
                de.Key = cvar.Name;
                d_Cvars[i] = de;
                i++;
            }

            //Grab Container
            StorageContainer container = GetContainer(); 

            //Create the XML Config File
            Config config = new Config(d_Cvars);
            FileStream fs = new FileStream(Path.Combine(container.Path, m_sConfig), FileMode.Create);
            XmlSerializer xs = new XmlSerializer(config.GetType());
            xs.Serialize(fs, config);
            fs.Close();

            Print("--- Saving Config ---", ConsoleLevel.DEBUG);
        }

        //*==========================*
        //*=    End of Config       =*
        //*==========================*


        /// <summary>
        /// Uses the British Keyboard layout to work out what key you pressed
        /// </summary>
        private AllowedInput[] m_sAInput =
        {
            new AllowedInput("D1","1","!"), new AllowedInput("D2","2","\""),
            new AllowedInput("D3","3","£"), new AllowedInput("D4","4","$"),
            new AllowedInput("D5","5","%"), new AllowedInput("D6","6","^"),
            new AllowedInput("D7","7","&"), new AllowedInput("D8","8","*"),
            new AllowedInput("D9","9","("), new AllowedInput("D0","0",")"),
            new AllowedInput("NumPad1","1","1"), new AllowedInput("NumPad2","2","2"),
            new AllowedInput("NumPad3","3","3"), new AllowedInput("NumPad4","4","4"),
            new AllowedInput("NumPad5","5","5"), new AllowedInput("NumPad6","6","6"),
            new AllowedInput("NumPad7","7","7"), new AllowedInput("NumPad8","8","8"),
            new AllowedInput("NumPad9","9","9"), new AllowedInput("NumPad0","0","0"),
            new AllowedInput("A","a","A"), new AllowedInput("B","b","B"),
            new AllowedInput("C","c","C"), new AllowedInput("D","d","D"),
            new AllowedInput("E","e","E"), new AllowedInput("F","f","F"),
            new AllowedInput("G","g","G"), new AllowedInput("H","h","H"),
            new AllowedInput("I","i","I"), new AllowedInput("J","j","J"),
            new AllowedInput("K","k","K"), new AllowedInput("L","l","L"),
            new AllowedInput("M","m","M"), new AllowedInput("N","n","N"),
            new AllowedInput("O","o","O"), new AllowedInput("P","p","P"),
            new AllowedInput("Q","q","Q"), new AllowedInput("R","r","R"),
            new AllowedInput("S","s","S"), new AllowedInput("T","t","T"),
            new AllowedInput("U","u","U"), new AllowedInput("V","v","V"),
            new AllowedInput("W","w","W"), new AllowedInput("X","x","X"),
            new AllowedInput("Y","y","Y"), new AllowedInput("Z","z","Z"),
            new AllowedInput("OemPipe","\\","|"), new AllowedInput("OemQuestion","/","?"),
            new AllowedInput("OemMinus","-","_"), new AllowedInput("OemPlus","=","+"),
            new AllowedInput("OemOpenBrackets","[","{"), new AllowedInput("OemCloseBrackets","]","}"),
            new AllowedInput("OemTilde","'","@"), new AllowedInput("OemQuotes","#","~"),
            new AllowedInput("OemComma",",","<"), new AllowedInput("OemPeriod",".",">"),
            new AllowedInput("Divide","/","/"), new AllowedInput("Multiply","*","*"),
            new AllowedInput("OemSemicolon",";",":"), new AllowedInput("Decimal",".","."), 
            new AllowedInput("Add","+","+"), new AllowedInput("Subtract","-","-"),
            new AllowedInput("OemQuestion","/","?"), new AllowedInput("Space"," "," "),
        };
    }
}
